package com.electronic.signer;

import com.alibaba.fastjson.JSON;
import com.electronic.domain.SignatureConfig;
import com.electronic.enums.AlgorithmEnum;
import com.electronic.gbt.GBT6_1_1.SESeal;
import com.electronic.gbt.GBT6_1_2_6.ExtData;
import com.electronic.gbt.GBT6_1_2_6.ExtensionDatas;
import com.electronic.gbt.GBT7_1_1.SES_Signature;
import com.electronic.gbt.GBT7_1_2.TBS_Sign;
import com.electronic.utils.GBTSignatureUtil;
import com.electronic.utils.SystemUtil;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.signatures.IExternalSignatureContainer;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.util.Arrays;

import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.Date;
import java.util.Locale;

/**
 * `PdfGmtSigner`类，实现了`IExternalSignatureContainer`接口，用于处理 PDF 文档的国密签名相关操作
 */
public class PdfGmtSigner implements IExternalSignatureContainer {
    /**
     * 签名配置信息，可获取
     */
    @Getter
    protected SignatureConfig signInfoConfig;
    /**
     * 签名证书数组
     */
    protected Certificate[] signCerts;
    /**
     * 签名证书
     */
    protected Certificate signCert = null;
    /**
     * 安全电子印章
     */
    @Setter
    protected SESeal seSeal;
    /**
     * 签名算法，可获取
     */
    @Getter
    private AlgorithmEnum algorithmEnum;
    /**
     * 估计的签名大小，可设置
     */
    @Setter
    private int estimatedSize = 40000;
    /**
     * 哈希值，可获取
     */
    @Getter
    private byte[] hashValue = null;

    /**
     * 私钥
     */
    protected PrivateKey privatekey;

    /**
     * 签名字典
     */
    private PdfDictionary sigDic;

    /**
     * 外部电子签名值
     */
    @Setter
    private byte[] externalSignatureValue;

    /**
     * 构造函数，初始化签名相关配置和数据
     *
     * @param signatureConfig 签名配置
     */
    public PdfGmtSigner(SignatureConfig signatureConfig) {
        this.signInfoConfig = signatureConfig;
        this.algorithmEnum = signatureConfig.getAlgorithmType();
        // 根据签名算法生成临时私钥
        if (AlgorithmEnum.SM3.getHashAlg().equals(this.algorithmEnum.getHashAlg())) {
            this.privatekey = SignatureConfig.getSm2PrivateKey();
        } else {
            this.privatekey = SignatureConfig.getRsaPrivateKey();
        }

        this.signCerts = signatureConfig.getCertChain();
        this.signCert = signatureConfig.getSigCert();

        //国标字典
        sigDic = new PdfDictionary();
        sigDic.put(PdfName.Filter, new PdfName("GM.PKILite"));
        sigDic.put(PdfName.SubFilter, new PdfName("GM.sm2seal"));
    }

    /**
     * 修改签名字典
     *
     * @param signDic 待修改的签名字典
     */
    @Override
    public void modifySigningDictionary(PdfDictionary signDic) {
        signDic.putAll(sigDic);
    }

    /**
     * 执行签名操作
     *
     * @param paramInputStream 输入流
     * @return 签名后的字节数组
     * @throws GeneralSecurityException 签名过程中的安全异常
     */
    @Override
    public byte[] sign(InputStream paramInputStream) throws GeneralSecurityException {
        try {
            if (!Arrays.isNullOrEmpty(this.externalSignatureValue)) {
                return externalSignatureValue;
            }
            // 构造 扩展信息
            ExtensionDatas extdatas = new ExtensionDatas();
            //添加证书链
            extdatas.add(new ExtData(
                    new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"),
                    ASN1Boolean.TRUE,
                    new DEROctetString(JSON.toJSONBytes(""))

            ));
            //添加系统硬盘序列号
            extdatas.add(new ExtData(
                    new ASN1ObjectIdentifier("1.2.840.113549.1.1.1"),
                    ASN1Boolean.TRUE,
                    new DEROctetString(JSON.toJSONBytes(SystemUtil.DiskSerialNumber()))

            ));
            //添加系统MAC地址
            extdatas.add(new ExtData(
                    new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"),
                    ASN1Boolean.TRUE,
                    new DEROctetString(JSON.toJSONBytes(SystemUtil.getMacAddress()))

            ));
            //添加CPU序列号
            extdatas.add(new ExtData(
                    new ASN1ObjectIdentifier("1.2.840.113549.1.1.3"),
                    ASN1Boolean.TRUE,
                    new DEROctetString(JSON.toJSONBytes(SystemUtil.getCpuSerialNumber()))

            ));
            //添加IP地址
            extdatas.add(new ExtData(
                    new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"),
                    ASN1Boolean.TRUE,
                    new DEROctetString(JSON.toJSONBytes(SystemUtil.getLocalIpAddress()))

            ));

            // 计算输入流的原文摘要
            this.hashValue = GBTSignatureUtil.buildSignDataDigest(IOUtils.toByteArray(paramInputStream), this.algorithmEnum);
            // 构造签章信息
            TBS_Sign tbs_sign = new TBS_Sign()
//                    .setVersion(this.seSeal.geteSealInfo().getHeader().getVersion())
                    .setVersion(new ASN1Integer(9))
                    .setEseal(this.seSeal)
                    .setTimeInfo(new ASN1GeneralizedTime(new Date(), Locale.CHINA))
                    .setDataHash(hashValue)
                    .setPropertyInfo("")
                    .setExtDatas(extdatas);
            byte[] signedValue = GBTSignatureUtil.buildSignedValue(this.privatekey, tbs_sign.getEncoded("DER"), this.algorithmEnum);

            // 构造 SES 签名
            SES_Signature signature = GBTSignatureUtil.buildSESSignature(tbs_sign, signedValue, this.signCert, this.algorithmEnum);
            byte[] signData = signature.getEncoded("DER");
            this.estimatedSize = signData.length;
            return signData;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取估计的签名大小
     *
     * @return 估计的签名大小
     */
    public int getEstimatedSize() {
        return this.estimatedSize * 2 + 2;
    }
}
